﻿using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Blazor;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Utils;
using EasyXaf.EntityDesigner.BusinessObjects.Entities;
using System.Collections;

namespace EasyXaf.EntityDesigner.Editors.EntityAssociationEditors;

public class EntityAssociationEditor : ListEditor, IComplexListEditor, ISupportUpdate
{
    private readonly Locker _refreshLocker = new();

    protected BlazorApplication Application { get; private set; }

    protected IServiceProvider ServiceProvider => Application.ServiceProvider;

    protected CollectionSourceBase CollectionSource { get; private set; }

    protected IObjectSpace ObjectSpace { get; private set; }

    protected EntityAssociationModel EntityAssociationModel => EntityAssociationAdapter?.Model;

    protected EntityAssociationAdapter EntityAssociationAdapter => (EntityAssociationAdapter)Control;

    protected IList SelectedEntities { get; set; } = Array.Empty<object>();

    public override object FocusedObject { get; set; }

    public override SelectionType SelectionType => SelectionType.MultipleSelection;

    public EntityAssociationEditor(IModelListView model)
        : base(model)
    {
        _refreshLocker.LockedChanged += RefreshLocker_LockedChanged;
    }

    public void BeginUpdate()
    {
        LockSelectionEvents();
        _refreshLocker.Lock();
    }

    public void EndUpdate()
    {
        UnlockSelectionEvents();
        _refreshLocker.Unlock();
    }

    public override IList GetSelectedObjects()
    {
        if (FocusedObject != null)
        {
            return new object[] { FocusedObject };
        }
        return SelectedEntities;
    }

    public override void Refresh()
    {
        if (_refreshLocker.Locked)
        {
            _refreshLocker.Call(nameof(Refresh));
        }
        else
        {
            RefreshCore();
        }
    }

    public virtual void UnselectAll()
    {
        EntityAssociationModel?.UnselectAll();
    }

    public void Setup(CollectionSourceBase collectionSource, XafApplication application)
    {
        Application = (BlazorApplication)application;
        CollectionSource = collectionSource;

        if (collectionSource != null)
        {
            ObjectSpace = collectionSource.ObjectSpace;

            collectionSource.CollectionChanged += CollectionSource_CollectionChanged;
            collectionSource.ObjectSpace.Committed += ObjectSpace_Committed;
        }
    }

    public override void Dispose()
    {
        if (CollectionSource != null)
        {
            CollectionSource.CollectionChanged -= CollectionSource_CollectionChanged;
            CollectionSource.ObjectSpace.Committed -= ObjectSpace_Committed;

            ObjectSpace = null;
            CollectionSource = null;
        }

       (Control as IDisposable)?.Dispose();
        _refreshLocker.LockedChanged -= RefreshLocker_LockedChanged;

        base.Dispose();

        GC.SuppressFinalize(this);
    }

    protected virtual void RefreshCore()
    {
        SelectedEntities.Clear();
        EntityAssociationModel?.Refresh();
        OnSelectionChanged();
    }

    protected override void AssignDataSourceToControl(object dataSource)
    {
    }

    protected override object CreateControlsCore()
    {
        return new EntityAssociationAdapter(new EntityAssociationModel
        {
            ObjectSpace = ObjectSpace
        });
    }

    protected override void OnControlsCreated()
    {
        base.OnControlsCreated();

        if (EntityAssociationModel != null)
        {
            EntityAssociationModel.QueryCollectionSource += EntityAssociationModel_QueryCollectionSource;
            EntityAssociationModel.SelectionChanged += EntityAssociationModel_SelectionChanged;
            EntityAssociationModel.EntityDoubleClicked += EntityAssociationModel_EntityDoubleClicked;
        }
    }

    public override void BreakLinksToControls()
    {
        if (EntityAssociationModel != null)
        {
            EntityAssociationModel.QueryCollectionSource -= EntityAssociationModel_QueryCollectionSource;
            EntityAssociationModel.SelectionChanged -= EntityAssociationModel_SelectionChanged;
            EntityAssociationModel.EntityDoubleClicked -= EntityAssociationModel_EntityDoubleClicked;
        }

        base.BreakLinksToControls();
    }

    private void RefreshLocker_LockedChanged(object sender, LockedChangedEventArgs e)
    {
        if (e.PendingCalls.Contains(nameof(Refresh)))
        {
            RefreshCore();
        }
    }

    private void CollectionSource_CollectionChanged(object sender, EventArgs e)
    {
        Refresh();
    }

    private void ObjectSpace_Committed(object sender, EventArgs e)
    {
        Refresh();
    }

    private void EntityAssociationModel_EntityDoubleClicked(object sender, EntityDoubleClickedEventArgs e)
    {
        if (e.Entity != null && e.Entity.GetType() != ObjectType)
        {
            return;
        }

        FocusedObject = ObjectSpace.GetObject(e.Entity);

        OnSelectionChanged();
        OnProcessSelectedItem();
    }

    private void EntityAssociationModel_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var selectedEntities = new List<Entity>();

        foreach (var entity in e.Entities)
        {
            if (entity.GetType() != ObjectType)
            {
                continue;
            }
            selectedEntities.Add(ObjectSpace.GetObject(entity));
        }

        FocusedObject = selectedEntities.FirstOrDefault();
        SelectedEntities = selectedEntities;

        OnSelectionChanged();
    }

    private void EntityAssociationModel_QueryCollectionSource(object sender, QueryCollectionSourceEventArgs e)
    {
        e.ObjectSpace = ObjectSpace;
        e.CollectionSource = CollectionSource.GetEnumerable(true).OfType<Entity>().ToList();
    }
}
